package com.dog.modules.websocket.config;

import com.alibaba.fastjson2.JSON;
import com.dog.modules.websocket.domain.WsPayload;
import com.dog.modules.websocket.domain.enums.ClientTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
public class WsSessionManager {

    private AtomicInteger opOnlineCount = new AtomicInteger(0);
    private AtomicInteger bizOnlineCount = new AtomicInteger(0);
    private AtomicInteger appOnlineCount = new AtomicInteger(0);

    /**
     * TODO 考虑集群部署情况  例如用Redis同步session数据
     * session池 按端分组
     * 在线用户的Map集合，<clientType,<sessionId,WebSocketSession>>
     */
    private Map<String, ConcurrentHashMap<String, WebSocketSession>> SESSION_POOL = new ConcurrentHashMap<>();


    /**
     * 添加 session
     *
     * @param group     session组
     * @param sessionId
     * @param session   WebSocketSession
     */
    public void add(String group, String sessionId, WebSocketSession session) {
        // 判断是否已经存在
        ConcurrentHashMap<String, WebSocketSession> sessionGroup = SESSION_POOL.get(group);
        if (Objects.isNull(sessionGroup)) {
            sessionGroup = new ConcurrentHashMap<>(20);
            SESSION_POOL.put(group, sessionGroup);
        }
        sessionGroup.put(sessionId, session);
        log.info("[{}] session added", sessionId);
        switch (ClientTypeEnum.getEnumByCode(group)) {
            case APP:
                appOnlineCount.incrementAndGet();
                break;
            case BIZ:
                bizOnlineCount.incrementAndGet();
                break;
            case OP:
                opOnlineCount.incrementAndGet();
                break;
            default:
                break;
        }
        log.info("session count: op[{}],biz[{}],app[{}]", opOnlineCount.get(), bizOnlineCount.get(), appOnlineCount.get());
    }

    /**
     * 删除并同步关闭连接
     *
     * @param group     session组
     * @param sessionId
     */
    public void removeAndClose(String group, String sessionId) {
        ConcurrentHashMap<String, WebSocketSession> sessionGroup = SESSION_POOL.get(group);
        if (sessionGroup == null) {
            log.info("[{}] session group has closed.", group);
            return;
        }
        WebSocketSession session = sessionGroup.get(sessionId);
        if (session == null) {
            log.info("[{}] session has closed.", sessionId);
            return;
        }
        synchronized (session) {
            try {
                // 关闭连接
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                sessionGroup.remove(sessionId);
            }
            log.info("[{}] session closed", sessionId);
            switch (ClientTypeEnum.getEnumByCode(group)) {
                case APP:
                    if (appOnlineCount.get() > 0) {
                        appOnlineCount.decrementAndGet();
                    }
                    break;
                case BIZ:
                    if (bizOnlineCount.get() > 0) {
                        bizOnlineCount.decrementAndGet();
                    }
                    break;
                case OP:
                    if (opOnlineCount.get() > 0) {
                        opOnlineCount.decrementAndGet();
                    }
                    break;
                default:
                    break;
            }
            log.info("session count: op[{}],biz[{}],app[{}]", opOnlineCount.get(), bizOnlineCount.get(), appOnlineCount.get());
        }
    }

    /**
     * 获得 session
     *
     * @param group     session组
     * @param sessionId
     * @return WebSocketSession
     */
    public WebSocketSession getSession(String group, String sessionId) {
        ConcurrentHashMap<String, WebSocketSession> sessionGroup = SESSION_POOL.get(group);
        if (sessionGroup == null) {
            log.info("[{}] the session group has closed.", group);
            return null;
        }
        WebSocketSession session = sessionGroup.get(sessionId);
        if (session == null) {
            log.info("[{}] session has closed.", sessionId);
            return null;
        }
        return session;
    }

    /**
     * 发送消息
     *
     * @param webSocketSession WebSocketSession
     * @param payload          消息
     */
    public void sendMessage(WebSocketSession webSocketSession, WsPayload payload) {
        if (payload == null) {
            return;
        }
        if (webSocketSession == null || !webSocketSession.isOpen()) {
            log.info("send to session[{}] msg :{} ,but is closed", webSocketSession == null ? "null" : webSocketSession.getId(), JSON.toJSONString(payload));
            return;
        }
        try {
            synchronized (webSocketSession) {
                String str = JSON.toJSONString(payload);
                log.info("send to session[{}] msg: {} ", webSocketSession.getId(), str);
                webSocketSession.sendMessage(new TextMessage(str));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void sendToClient(String group, String sessionId, WsPayload json) {
        WebSocketSession webSocketSession = getSession(group, sessionId);
        sendMessage(webSocketSession, json);
    }

    public void sendToClientGroup(String id, WsPayload json) {
        ConcurrentHashMap<String, WebSocketSession> sessionMap = SESSION_POOL.get(id);
        Set<String> set = sessionMap.keySet();
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            WebSocketSession webSocketSession = sessionMap.get(key);
            sendMessage(webSocketSession, json);
        }
    }

    public void sendToAll(WsPayload json) {
        Set<String> set = SESSION_POOL.keySet();
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            SESSION_POOL.get(key);
            sendToClientGroup(key, json);
        }
    }

}
